Skip to content

Maui Blazor image cache is not working #8279

Closed

Description

@romanov

Description

Hi everyone! When switching between pages in the app, images aren't loaded from the cache but are reloaded from the server (local folder). Visually, there's a brief flash, followed by the image loading when the page is reloaded. In other words, the image cache isn't working.

android1

Manifest (or serviceworker) cache is not helping.

Steps to Reproduce

  1. Create MAUI Blazor App
  2. Add any static (wwwroot) images to any other page
  3. Navigate to that page
  4. Navigate back to the homepage
  5. Navigate to that page again and u still see blinking

Version with bug

6.0.400 (current)

Last version that worked well

Unknown/Other

Affected platforms

I was not able test on other platforms

Affected platform versions

Android (Emulator and real device)

Did you find any workaround?

No

Relevant log output

No response

Activity

  1. added
    s/needs-verificationIndicates that this issue needs initial verification before further triage will happen
    t/bugSomething isn't working
    on Jun 23, 2022
  2. javiercn commented on Jun 23, 2022

    @javiercn
    Member

    @romanov thanks for contacting us.

    Does the same webview instance survive across navigations?

  3. added
    s/needs-infoIssue needs more info from the author
    and removed
    s/needs-verificationIndicates that this issue needs initial verification before further triage will happen
    on Jun 23, 2022
  4. romanov commented on Jun 23, 2022

    @romanov
    Author

    @romanov thanks for contacting us.

    Does the same webview instance survive across navigations?

    How to check it? It's default MAUI Blazor template with default BlazorWebView

  5. ghost added
    s/needs-attentionIssue has more information and needs another look
    on Jun 23, 2022
  6. ghost removed
    s/needs-infoIssue needs more info from the author
    on Jun 23, 2022
  7. javiercn commented on Jun 24, 2022

    @javiercn
    Member

    @romanov put a static counter variable in your blazor app, increment it every time index is rendered and display the results. That's a good proxy to determine if the blazor webview component is being recreated

  8. added
    s/needs-infoIssue needs more info from the author
    and removed
    s/needs-attentionIssue has more information and needs another look
    on Jun 24, 2022
  9. romanov commented on Jun 25, 2022

    @romanov
    Author

    What is the best method to put the counter's incementation?

    protected override async Task OnInitializedAsync() or OnInitialized?

    Is it right for a blazor page? Or they call everytime on navigation?

    I've just tested a blank new MAUI Blazor template and added image to the second view. Same reload.

  10. 39 remaining items

  11. Kebechet commented on Nov 13, 2024

    @Kebechet
    Contributor

    yes, we also have Maui hybrid in production and this really hurts the overall feel of the app

  12. Kebechet commented on Jan 18, 2025

    @Kebechet
    Contributor

    @mkArtakMSFT is there a chance this will be resolved in some .NET 9 patch release ?

  13. TonyHenrique commented on Jan 18, 2025

    @TonyHenrique

    It would be great to see this fixed.

  14. monkeyK1n9 commented on May 30, 2025

    @monkeyK1n9

    Coming here in 2025, to ask for a fix too, please.

  15. Kebechet commented on Jun 6, 2025

    @Kebechet
    Contributor

    @jfversluis could this be pushed to NET10 MAUI ?

  16. trajkovdimitar commented on Jul 3, 2025

    @trajkovdimitar

    Please just retire this technology, if no one is working on it to be usable product.

  17. Kebechet commented on Apr 1, 2026

    @Kebechet
    Contributor

    The work we do dotnet/aspnetcore#52824 should simplify this for us. But for now, moving to Backlog.

    @javiercn @mkArtakMSFT can this be prioritized? Or are there any workarounds available? Our Blazor hybrid app is image-heavy and this is a real problem for us

  18. monkeyK1n9 commented on Apr 1, 2026

    @monkeyK1n9

    I have found a workaround for my app.
    I use IMemoryCache with the key being the url and value the base64 string of the image.
    I have an IImageService that will fetch out this image if present. or from the web if not.
    Invalidate and cache duration and other options are also to be provided.

    // in program.cs, add the memory cache service and your image cache service
    services.AddMemoryCache();
    services.AddTransient<ImageCacheService>();
    
    // in your ImageCacheService
        public class ImageCacheService
        {
            private readonly IMemoryCache _cache;
            private readonly IFileStorageService _fileStorageService; // if you ever store the image locally instead
            private readonly IApiClient _apiClient; // to fetch my image, you can hit the endpoint differently in yours
            private readonly MemoryCacheEntryOptions _cacheOptions;
            private readonly ILogger<ImageCacheService> _logger;
            
            private const int BUFFER_SIZE = 512 * 1024; // 16KB buffer size for streaming
            
            public ImageCacheService(
                IMemoryCache cache, 
                IFileStorageService fileStorageService, 
                IApiClient apiClient, 
                ILogger<ImageCacheService> logger)
            {
                _cache = cache;
                _fileStorageService = fileStorageService;
                _apiClient = apiClient;
                _logger = logger;
    
                _cacheOptions = new MemoryCacheEntryOptions
                {
                    AbsoluteExpiration = DateTimeOffset.Now.AddHours(4), // my cache lives for 4hours max
                };
            }
    
        // you code to get cached value, invalidate or what so ever
        public async Task<Result<(byte[] imageContent, string contentType)?>> GetAsync(string imageUrl, CancellationToken token = default)
        public Task RemoveAsync(string imageUrl, CancellationToken token = default)
        public Task ClearCacheAsync()
    
    // in you razor, you can inject the service
    // the best thing to do is to create a create a custom image component that will retrieve the cached base64 value first then use it as the data string for the <img /> tag
    
    @inject IImageCacheService ImageCache
    
    @if (string.IsNullOrEmpty(imageData))
    {
        // show something
    }
    else
    {
        <img src=......
    }
    
    @code
    {
        private string imageData {get; set;} = string.Empty;
        [Parameter]
        public string ImageUrl {get; set;}
    
        protected override async OnInitializedAsync()
        {
             imageData = await ImageCache.GetAsync(ImageUrl);
        }

    I hope this is helpful enough. If you need more assistance, let me know.
    I am even open to some freelancing 😆

  19. Kebechet commented on Jun 2, 2026

    @Kebechet
    Contributor

    I looked into this. Sharing the root cause and a proposed approach.

    Root cause

    BlazorWebView serves every static asset from the app content root with the response header:

    Cache-Control: no-cache, max-age=0, must-revalidate, no-store
    

    The no-store directive tells the WebView not to cache anything, so static images (and fonts/CSS) are re-fetched and re-decoded every time a page re-renders them — that re-decode is the visible flicker described here. Manifest/service-worker caching can't override a no-store coming from the response.

    The header is set on every platform:

    • iOS/MacCatalyst — hardcoded in BlazorWebViewHandler.iOS.cs
    • Windows — hardcoded in StaticContentProvider.GetResponseHeaders
    • Android — inherited from the upstream WebViewManager base class

    It was introduced with the original BlazorWebView port and was meant to keep user scripts from being served stale from the WebView cache (the clearer sibling comment in HybridWebView reads "Disable local caching which would otherwise prevent user scripts from executing correctly"). That concern is about executable content, but the header is applied to all responses, including inert static assets.

    Proposed approach

    Changing the default to allow caching would be a behavioral breaking change: the WebView cache persists across app updates on all three platforms (Android cache dir, iOS WKWebsiteDataStore, Windows WebView2 user-data folder), so a bundled asset changed in an update could be served stale.

    So instead of changing the default, the PR adds an opt-in, additive hook that lets an app choose the Cache-Control value per resource, with no effect on existing apps:

    blazorWebView.StaticContentCacheControlProvider = request =>
        request.ContentType.StartsWith("image/", StringComparison.Ordinal)
            ? "max-age=86400"
            : null; // null keeps the default no-store

    This resolves the flicker for apps that opt in (e.g. caching images) while keeping framework scripts uncached, and gives full per-resource control. Implemented for Android, iOS/MacCatalyst, and Windows.

    PR (draft): #35706

    If the team would rather also flip the default (opt-out instead of opt-in), the same hook works as the escape hatch — happy to adjust based on what maintainers prefer.

  20. monkeyK1n9 commented on Jun 3, 2026

    @monkeyK1n9

    @Kebechet please can you show a demo ?

  21. kubaflo commented on Oct 10, 2026

    @kubaflo
    Contributor

    Thank you for the report and the investigation details. We are closing this as completed: the .NET 11 development branch implements a bounded per-WebView response cache for the reported reload case when explicitly enabled. Caching remains opt-in, the default no-store behavior is unchanged, and this is not a claim of a .NET MAUI 10 servicing fix.

    Supporting evidence: resolution evidence 1.

    This records the merged source change; it does not imply that the fix is present in every released .NET MAUI package.

  22. locked and limited conversation to collaborators on Nov 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Priority:1Created by mkArtakMSFTarea-blazorBlazor Hybrid / Desktop, BlazorWebViewdiscussedCreated by mkArtakMSFT to help with planning temporarily. It will be removed after planning is done.s/triagedIssue has been revieweds/verifiedVerified / Reproducible Issue ready for Engineering Triaget/bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions