Open links in the default browser

    Your application can open document links in the user's default browser while keeping the editor available for writing. Use HtmlElementClicked and FireHtmlElementClickEventFor to handle link clicks, and check the editor mode before launching a URL.

    Diagram showing a document link clicked, the application checking its target, and the operating system opening an allowed URL in the default browser.
    Diagram showing a document link clicked, the application checking its target, and the operating system opening an allowed URL in the default browser.

    The problem

    Design mode vs. Preview mode

    HtmlElementClicked reports clicks in the rendered design or preview document. HTML source editing has no rendered DOM click target. Check EditorMode when deciding whether to navigate or continue authoring.

    The hook: HtmlElementClicked + FireHtmlElementClickEventFor

    The WPF editor exposes a CLR event HtmlElementClicked on WpfHtmlEditor whose args (SpiceLogic.HtmlEditor.WPF.EditorEventArgs.HtmlElementClickedEventArgs, derived from RoutedEventArgs) carry the clicked DOM element as ClickedElement (an mshtml.IHTMLElement), the raw event payload as EventData (IHTMLEventObj), 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 MyEditor.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 WPF snippet

    using System;
    using System.Diagnostics;
    using SpiceLogic.HtmlEditor.Abstractions;
    using SpiceLogic.HtmlEditor.WPF.EditorEventArgs;
    
    // In your Window / UserControl Loaded handler:
    MyEditor.Options.FireHtmlElementClickEventFor = HtmlElementTypes.Hyperlink;
    MyEditor.HtmlElementClicked += MyEditor_HyperlinkClicked;
    private void MyEditor_HyperlinkClicked(object sender, HtmlElementClickedEventArgs e)
    {
        // Only launch in Preview mode - never while the user edits.
        if (MyEditor.EditorMode != EditorModes.ReadOnlyPreview)
            return;
        if (e.ElementType != HtmlElementTypes.Hyperlink || e.ClickedElement == null)
            return;
        string href = e.ClickedElement.getAttribute("href", 0) as string;
        if (!TryGetSafeExternalUrl(href, out string safeUrl))
            return;
        e.Handled = true; // stop routed event
        // 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.WPF.EditorEventArgs
    
    ' In your Window / UserControl Loaded handler:
    MyEditor.Options.FireHtmlElementClickEventFor = HtmlElementTypes.Hyperlink
    AddHandler MyEditor.HtmlElementClicked, AddressOf MyEditor_HyperlinkClicked
    
    Private Sub MyEditor_HyperlinkClicked(sender As Object, e As HtmlElementClickedEventArgs)
        ' Only launch in Preview mode - never while the user edits.
        If MyEditor.EditorMode <> EditorModes.ReadOnlyPreview Then Return
        If e.ElementType <> HtmlElementTypes.Hyperlink OrElse e.ClickedElement Is Nothing Then Return
        Dim href As String = TryCast(e.ClickedElement.getAttribute("href", 0), String)
        Dim safeUrl As String = Nothing
        If Not TryGetSafeExternalUrl(href, safeUrl) Then Return
        e.Handled = True ' stop routed event
        ' 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

    WinForms variant (same idea, different event-args)

    If you also ship the WinForms control, the same pattern applies on WinFormHtmlEditor. The differences: the event args type is SpiceLogic.HtmlEditor.WinForms.Models.BOs.EditorEventArgs.HtmlElementClickedEventArgs, ClickedElement is a System.Windows.Forms.HtmlElement so you read the href via e.ClickedElement.GetAttribute("href"), and to cancel the default navigation you set e.EventData.ReturnValue = false; instead of e.Handled = true;. The TryGetSafeExternalUrl helper is reusable verbatim.

    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 to 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 MyEditor.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?

    An older page in the WinForms Programming category describes the same idea using Internet Explorer era terminology. This page is the current, cross-product reference - use the HtmlElementClicked + Options.FireHtmlElementClickEventFor + UseShellExecute = true pattern documented here for both controls, and treat the legacy page as historical context only.

    Ask your AI to do this

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

    I use the SpiceLogic WPF HTML Editor. Help me open approved hyperlinks in the Windows default browser from preview mode. Confirm the exact API names, types and defaults with the SpiceLogic MCP documentation, then show the changes for my project.

    Last updated on Oct 10, 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.