See the question and my original answer on StackOverflow

A site can define a Favicon (or not) using many ways, so here is a code that directly uses WebView2's FavIcon features: the FaviconChanged Event the FaviconUri Property and the GetFaviconAsync() Method.

So you can just use this:

tabViewItem.IconSource = new BitmapIconSource
    UriSource = new Uri(MyWebView.CoreWebView2.FaviconUri),
    ShowAsMonochrome = false,

I've been one step further in my context, as I wanted to use the favicon for the application icon too. This uses WinUI3's AppWindow.SetIcon method.

The tricky part is AppWindow.SetIcon only supports a file path to a .ico format file while WebView2 only support PNG or JPG streams. Luckily, .ico files can be encoded using PNG bytes (since Windows Vista).



        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />

    <StackPanel Orientation="Horizontal">
        <TextBox x:Name="url" />
        <Button x:Name="navigate" Click="navigate_Click">Go</Button>

        VerticalAlignment="Stretch" />



In the following code, I avoid rewriting the .ico for a given favicon uri each time it changes but this is up to you.

public MainWindow()

    MyWebView.NavigationCompleted += MyWebView_NavigationCompleted;

private async void navigate_Click(object sender, RoutedEventArgs e)
    await MyWebView.EnsureCoreWebView2Async();

private void MyWebView_NavigationCompleted(WebView2 sender, CoreWebView2NavigationCompletedEventArgs args)
    // unhook from ourselves, we shouldn't need it anymore
    MyWebView.NavigationCompleted -= MyWebView_NavigationCompleted;
    // hook on favicon changes
    MyWebView.CoreWebView2.FaviconChanged += (s, e) => _ = UpdateFavicon();

    // first time set
    _ = UpdateFavicon();

private async ValueTask UpdateFavicon()
    var uri = MyWebView.CoreWebView2.FaviconUri;

    // in your TabViewItem case, this is where you would do
    // tabViewItem.IconSource = new BitmapIconSource...

    if (string.IsNullOrEmpty(uri))
        // Note: when the site has no favicon (like with,
        // uri is the empty string and GetFaviconAsync will return an 
        // "undefined" icon stream that can still be used to save a .ico.
        // Here I just remove the icon but we could continue
        // and use this "undefined" icon instead.

    // come up with a unique (cache) file path
    using var md5 = MD5.Create();
    var name = new Guid(md5.ComputeHash(Encoding.UTF8.GetBytes(uri))) + ".ico";
    var path = Path.Combine(Path.GetTempPath(), name);

    // don't rewrite if already there
    if (!File.Exists(path))
        // get PNG bytes
        using var iconStream = await MyWebView.CoreWebView2.GetFaviconAsync(CoreWebView2FaviconImageFormat.Png);

        // write to memory (shouldn't be too big)
        using var ms = new MemoryStream();
        using var stream = iconStream.AsStream();
        await stream.CopyToAsync(ms);
        stream.Position = 0;

        // decode it to determine width & height
        // seems WebView2 sends always 16x16 streams but not sure it's contractual
        var decoder = await BitmapDecoder.CreateAsync(ms.AsRandomAccessStream());

        // write icon in PNG format
        WritePNGIcon(path, decoder.PixelWidth, decoder.PixelHeight, ms.ToArray());

public static void WritePNGIcon(string iconFilePath, uint width, uint height, byte[] pngBytes, int pngBytesIndex = 0, int pngBytesCount = int.MaxValue)
    using var stream = File.OpenWrite(iconFilePath);
    WritePNGIcon(stream, width, height, pngBytes, pngBytesIndex, pngBytesCount);

public static void WritePNGIcon(Stream iconFileStream, uint width, uint height, byte[] pngBytes, int pngBytesIndex = 0, int pngBytesCount = int.MaxValue)
    if (pngBytesCount == int.MaxValue || pngBytesCount < 0)
        pngBytesCount = pngBytes.Length;

    var startPos = iconFileStream.Position;
    var writer = new BinaryWriter(iconFileStream);
    writer.Write((short)0); // reserved
    writer.Write((short)1); // image type (1 for .ico)
    writer.Write((short)1); // count
    const int maxIconSize = 256;
    writer.Write(width == maxIconSize ? (byte)0 : (byte)width);
    writer.Write(height == maxIconSize ? (byte)0 : (byte)height);
    writer.Write((byte)0); // number of colors in the color palette
    writer.Write((byte)0); // reserved
    writer.Write((short)1); // color planes
    writer.Write((short)32); // bits per pixel (ARGB=32)

    var offsets = iconFileStream.Position;
    writer.Write(0); // size of image data, we'll rewrite that later
    writer.Write(0); // offset of data from beginning;

    var pngOffset = iconFileStream.Position - startPos;
    writer.Write(pngBytes, pngBytesIndex, pngBytesCount);

    // save end position
    var endPos = iconFileStream.Position;
    iconFileStream.Seek(offsets, SeekOrigin.Begin);
    iconFileStream.Position = endPos; // restore end position

Results on

enter image description here

Note: the .ico path can also be used as the UriSource for the TabViewItem.