Open links in the default browser

    The editor opens a clicked link in the user's default browser or mail app: a click in preview, Ctrl+Click while editing. Handle HtmlElementClicked only when you want your own links to open your own screens.

    Diagram: a link clicked in preview, or Ctrl+Clicked while editing, passes through the editor link rule (web and mail links only) and opens in the default browser or mail app while the editor keeps the document
    Diagram: a link clicked in preview, or Ctrl+Clicked while editing, passes through the editor link rule (web and mail links only) and opens in the default browser or mail app while the editor keeps the document

    Built in: no code needed

    Customers want a clicked hyperlink to open in the user's own default browser, not inside the editor. The editor already does that in two places:

    • While editing (EditorModes.WysiwygDesign): a plain click places the caret in the link so the user can change it, and Ctrl+Click opens the link in the default browser.
    • In preview (EditorModes.ReadOnlyPreview): every click opens the link in the default browser, and the editor stays on your document.

    Only web (http, https) and mail (mailto) addresses are opened. Other addresses, such as file: paths or custom protocols, are never handed to Windows, because a document can name a program there, and Ctrl+Click follows the same rule.

    In EditorModes.HtmlEdit the user edits raw text, so there is nothing to click. Write code only when you want different behaviour on the editing surface, for example opening a link with a plain click, or routing your own app links to your own screens. HtmlElementClicked is raised for clicks on the editing surface only; it does not fire in preview mode, where the built-in handoff to the default browser applies.

    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;.

    The snippet below makes a plain click on a link open it while the user edits (by default that takes Ctrl+Click). Inside the handler, cancel the default click handling 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)
    {
        // Raised on the editing surface: open the link with a plain click while editing.
        if (htmlEditor1.EditorMode != EditorModes.WysiwygDesign)
            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)
        ' Raised on the editing surface: open the link with a plain click while editing.
        If htmlEditor1.EditorMode <> EditorModes.WysiwygDesign 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 you set e.EventData.returnValue = false;. WPF preview mode opens links in the default browser the same way. 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 routes through whichever browser the user has set as their Windows default.

    Ask your AI to do this

    Connect the SpiceLogic MCP server, then give your coding assistant this prompt.

    I use the SpiceLogic WinForms HTML Editor. Preview links and Ctrl+Click already open in the default browser. Help me make a plain click open approved links while editing, or route my own link scheme to my screens with HtmlElementClicked. Confirm the exact API names, types and defaults with the SpiceLogic MCP documentation, then show the changes for my project.

    Last updated on Nov 5, 2026

    Put this into practice.

    Clone the C# and VB.NET sample projects from GitHub. Start with Quickstart, then explore the feature samples.

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