dotnet / dotnet/aspnetcore

API Proposal: Media components (Image, Video, FileDownload)

Open
#66,391 2 comments 4 reactions 0 assignees View on GitHub
api-proposal api-suggestion area-blazor
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

## Background and Motivation

Blazor has no built-in way to display images, videos, or trigger file downloads from non-HTTP sources (byte arrays, streams, database BLOBs). Developers currently must base64-encode data (causing large string allocations) or create separate HTTP endpoints (which can interfere with authorization). Issue #25274 (20+ upvotes) has requested an Image component since 2020. This feature introduces a suite of media components using streaming JS interop and browser Cache Storage API for efficient data transfer.

PRs #63360 (Image), #63540 (Media Suite — Video, FileDownload, MediaComponentBase, MediaSource, MediaContext), and #67130 (extract media components into a standalone `Microsoft.AspNetCore.Components.Media` package). Related to issue #25274.

## Proposed API

The media components ship in a new standalone package, `Microsoft.AspNetCore.Components.Media`, under the matching `Microsoft.AspNetCore.Components.Media` namespace. The package depends only on `Microsoft.AspNetCore.Components` and `Microsoft.JSInterop` — it has no dependency on `Microsoft.AspNetCore.Components.Web`, so it can be consumed from any Blazor renderer (Web, native, custom hosts).

```diff
+ namespace Microsoft.AspNetCore.Components.Media;
+
+ public class MediaSource
+ {
+ public MediaSource(byte[] data, string mimeType, string cacheKey);
+ public MediaSource(Stream stream, string mimeType, string cacheKey);
+ public string CacheKey { get; }
+ public long? Length { get; }
+ public string MimeType { get; }
+ public Stream Stream { get; }
+ }
+
+ public class MediaContext
+ {
+ public ElementReference Element { get; set; }
+ public bool HasError { get; }
+ public bool IsLoading { get; }
+ public string? ObjectUrl { get; }
+ }
+
+ public abstract class MediaComponentBase : ComponentBase, IAsyncDisposable
+ {
+ public Dictionary? AdditionalAttributes { get; set; }
+ public MediaSource Source { get; set; }
+ public ValueTask DisposeAsync();
+ }
+
+ public class Image : MediaComponentBase
+ {
+ public RenderFragment? ChildContent { get; set; }
+ }
+
+ public class Video : MediaComponentBase
+ {
+ public RenderFragment? ChildContent { get; set; }
+ }
+
+ public class FileDownload : ComponentBase
+ {
+ public RenderFragment? ChildContent { get; set; }
+ public string FileName { get; set; }
+ public string? Text { get; set; }
+ }
+
+ public class FileDownloadContext
+ {
+ public string FileName { get; }
+ public Task InvokeAsync();
+ }
```

## Packaging

- **New package:** `Microsoft.AspNetCore.Components.Media`
- **Namespace:** `Microsoft.AspNetCore.Components.Media` (changed from the originally proposed `Microsoft.AspNetCore.Components.Web.Media`)
- **Dependencies:** `Microsoft.AspNetCore.Components`, `Microsoft.JSInterop` (no dependency on `Microsoft.AspNetCore.Components.Web`)
- Consumers add a `PackageReference` to `Microsoft.AspNetCore.Components.Media` and an `@using Microsoft.AspNetCore.Components.Media` to pick up the components.

## Usage Examples

Image from byte array:

```razor
@using Microsoft.AspNetCore.Components.Media

Product photo
```

Video from stream:

```razor
@using Microsoft.AspNetCore.Components.Media

```

File download:

```razor
@using Microsoft.AspNetCore.Components.Media

```

## Alternative Designs

Base64 encoding (allocates large strings), creating separate HTTP endpoints (interferes with authorization, requires more work). The streaming approach avoids large .NET-side memory allocations and uses browser Cache Storage for repeat loads.

## Risks

MediaSource is single-use (one instance per media load). The browser Cache Storage API is used for caching, which has storage limits depending on the browser.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.