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.

    The editor hosts inside the legacy IE WebBrowser control (MSHTML), and by default a clicked hyperlink resolves inside that hosted browser. Customers want the opposite behaviour: the click should launch the URL in the user's chosen Windows default browser, not in 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 only fires in WysiwygDesign and ReadOnlyPreview mode - the editor never attaches a click listener while in HtmlEdit mode, since there is no DOM to click there. Gate the launch behaviour on htmlEditor1.EditorMode == EditorModes.ReadOnlyPreview so the user is never pulled out of the editor mid-edit while in WysiwygDesign.

    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
        // UseShellExecute = true routes through the OS default browser.
        Process.Start(new ProcessStartInfo { FileName = safeUrl, UseShellExecute = true });
    }
    
    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;
    }
    Imports System
    Imports System.Diagnostics
    Imports SpiceLogic.HtmlEditor.Abstractions
    Imports SpiceLogic.HtmlEditor.WinForms.Models.BOs.EditorEventArgs
    
    ' In your form Load handler:
    htmlEditor1.Options.FireHtmlElementClickEventFor = HtmlElementTypes.Hyperlink
    AddHandler htmlEditor1.HtmlElementClicked, AddressOf HtmlEditor1_HyperlinkClicked
    
    Private Sub HtmlEditor1_HyperlinkClicked(sender As Object, e As HtmlElementClickedEventArgs)
        ' Only launch in Preview mode - never while the user edits.
        If htmlEditor1.EditorMode <> EditorModes.ReadOnlyPreview Then Return
        If e.ElementType <> HtmlElementTypes.Hyperlink OrElse e.ClickedElement Is Nothing Then Return
        Dim href As String = e.ClickedElement.GetAttribute("href")
        Dim safeUrl As String = Nothing
        If Not TryGetSafeExternalUrl(href, safeUrl) Then Return
        If e.EventData IsNot Nothing Then e.EventData.ReturnValue = False ' cancel in-host nav
        ' UseShellExecute = True routes through the OS default browser.
        Process.Start(New ProcessStartInfo With {.FileName = safeUrl, .UseShellExecute = True})
    End Sub
    
    Private Shared Function TryGetSafeExternalUrl(href As String, ByRef url As String) As Boolean
        url = Nothing
        If String.IsNullOrWhiteSpace(href) Then Return False
        If href.StartsWith("#") Then Return False ' in-page anchor
        If href.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase) Then Return False ' script injection
        Dim parsed As Uri = Nothing
        If Not Uri.TryCreate(href, UriKind.Absolute, parsed) Then Return False
        If parsed.Scheme = Uri.UriSchemeHttp OrElse parsed.Scheme = Uri.UriSchemeHttps OrElse
                parsed.Scheme = Uri.UriSchemeMailto OrElse parsed.Scheme = "tel" Then
            url = parsed.AbsoluteUri
            Return True
        End If
        Return False
    End Function

    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.UriSchemeFile if you want them, but treat any file: href from untrusted HTML with suspicion.
    • Relative URLs (about-us.html) - rejected by UriKind.Absolute; combine with htmlEditor1.BaseUrl before 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.

    Ask your AI to do this

    Let your assistant do this for you. With the SpiceLogic MCP server connected, paste this into Claude Code, Cursor, or VS Code Copilot in agent mode.

    Using the SpiceLogic WinForms HTML Editor already referenced in my project, make hyperlink clicks open in the user's Windows default browser instead of navigating inside the editor's hosted browser control, but only while the editor is in ReadOnlyPreview mode, for example a support link inside a shared quote document. Wire up Options.FireHtmlElementClickEventFor for hyperlinks, handle the HtmlElementClicked event, cancel the default in-host navigation, and launch the URL with Process.Start using UseShellExecute set to true. Confirm the exact event, args, and options member names with the SpiceLogic MCP tools before writing any code.

    Last updated on May 14, 2026

    Put this into practice.

    .NET WinForms HTML Editor Control ships with free C# and VB.NET sample projects and a 14-day evaluation.

    Prefer a guided look? Book a free live demo with our engineers - live on Zoom or Teams, never a chatbot.