diff --git a/src/Components/Endpoints/src/Builder/EndpointRouteBuilderResourceCollectionExtensions.cs b/src/Components/Endpoints/src/Builder/EndpointRouteBuilderResourceCollectionExtensions.cs
new file mode 100644
index 000000000000..3ae033a72223
--- /dev/null
+++ b/src/Components/Endpoints/src/Builder/EndpointRouteBuilderResourceCollectionExtensions.cs
@@ -0,0 +1,62 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Linq;
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.Endpoints;
+using Microsoft.AspNetCore.Routing;
+
+namespace Microsoft.AspNetCore.Builder;
+
+///
+/// Extensions for to add resource collection metadata.
+///
+public static class EndpointConventionBuilderResourceCollectionExtensions
+{
+ ///
+ /// Provides a helper to attach ResourceCollection metadata to endpoints.
+ ///
+ /// The .
+ /// The to resolve static assets from.
+ /// The manifest associated with the assets.
+ /// The that can be used to further configure the endpoints.
+ ///
+ /// This method attaches static asset metadata to endpoints. It provides a simplified way to add
+ /// resource collection metadata to any endpoint convention builder.
+ /// The must match the path of the manifest file provided to
+ /// the call.
+ ///
+ public static TBuilder WithStaticAssets(
+ this TBuilder builder,
+ IEndpointRouteBuilder endpoints,
+ string? manifestPath = null)
+ where TBuilder : IEndpointConventionBuilder
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+ ArgumentNullException.ThrowIfNull(endpoints);
+
+ var resolver = new ResourceCollectionResolver(endpoints);
+
+ builder.Add(endpointBuilder =>
+ {
+ // Check if there's already a resource collection on the metadata
+ if (endpointBuilder.Metadata.OfType().Any())
+ {
+ return;
+ }
+
+ // Only add metadata if static assets are registered
+ if (resolver.IsRegistered(manifestPath))
+ {
+ var collection = resolver.ResolveResourceCollection(manifestPath);
+ var importMap = ImportMapDefinition.FromResourceCollection(collection);
+
+ endpointBuilder.Metadata.Add(collection);
+ endpointBuilder.Metadata.Add(new ResourcePreloadCollection(collection));
+ endpointBuilder.Metadata.Add(importMap);
+ }
+ });
+
+ return builder;
+ }
+}
\ No newline at end of file
diff --git a/src/Components/Endpoints/src/PublicAPI.Unshipped.txt b/src/Components/Endpoints/src/PublicAPI.Unshipped.txt
index 424ca155339b..3034f18f89aa 100644
--- a/src/Components/Endpoints/src/PublicAPI.Unshipped.txt
+++ b/src/Components/Endpoints/src/PublicAPI.Unshipped.txt
@@ -1,4 +1,6 @@
#nullable enable
+Microsoft.AspNetCore.Builder.EndpointConventionBuilderResourceCollectionExtensions
+static Microsoft.AspNetCore.Builder.EndpointConventionBuilderResourceCollectionExtensions.WithStaticAssets(this TBuilder builder, Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string? manifestPath = null) -> TBuilder
Microsoft.AspNetCore.Components.ResourcePreloader
Microsoft.AspNetCore.Components.ResourcePreloader.ResourcePreloader() -> void
Microsoft.Extensions.DependencyInjection.RazorComponentsRazorComponentBuilderExtensions
diff --git a/src/Components/Endpoints/test/Builder/EndpointConventionBuilderResourceCollectionExtensionsTest.cs b/src/Components/Endpoints/test/Builder/EndpointConventionBuilderResourceCollectionExtensionsTest.cs
new file mode 100644
index 000000000000..2ce2cf100ad3
--- /dev/null
+++ b/src/Components/Endpoints/test/Builder/EndpointConventionBuilderResourceCollectionExtensionsTest.cs
@@ -0,0 +1,187 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.Endpoints;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.FileProviders;
+
+namespace Microsoft.AspNetCore.Components.Endpoints.Tests.Builder;
+
+public class EndpointConventionBuilderResourceCollectionExtensionsTest
+{
+ [Fact]
+ public void WithStaticAssets_DoesNotAddResourceCollection_ToEndpoints_NoStaticAssetsMapped()
+ {
+ // Arrange
+ var endpointBuilder = new TestEndpointRouteBuilder();
+ var conventionBuilder = new TestEndpointConventionBuilder();
+
+ // Act
+ conventionBuilder.WithStaticAssets(endpointBuilder);
+
+ // Assert
+ var endpointBuilderInstance = new TestEndpointBuilder();
+ conventionBuilder.ApplyConventions(endpointBuilderInstance);
+
+ var metadata = endpointBuilderInstance.Metadata.OfType().FirstOrDefault();
+ Assert.Null(metadata);
+ }
+
+ [Fact]
+ public void WithStaticAssets_AddsResourceCollection_ToEndpoints_WithMatchingManifest()
+ {
+ // Arrange
+ var endpointBuilder = new TestEndpointRouteBuilder();
+ endpointBuilder.MapStaticAssets("TestManifests/Test.staticwebassets.endpoints.json");
+ var conventionBuilder = new TestEndpointConventionBuilder();
+
+ // Act
+ conventionBuilder.WithStaticAssets(endpointBuilder, "TestManifests/Test.staticwebassets.endpoints.json");
+
+ // Assert
+ var endpointBuilderInstance = new TestEndpointBuilder();
+ conventionBuilder.ApplyConventions(endpointBuilderInstance);
+
+ var collection = endpointBuilderInstance.Metadata.OfType().FirstOrDefault();
+ Assert.NotNull(collection);
+
+ var list = Assert.IsAssignableFrom>(collection);
+ Assert.Single(list);
+ Assert.Equal("named.css", list[0].Url);
+
+ // Verify other metadata is also added
+ var preloadCollection = endpointBuilderInstance.Metadata.OfType().FirstOrDefault();
+ Assert.NotNull(preloadCollection);
+
+ var importMap = endpointBuilderInstance.Metadata.OfType().FirstOrDefault();
+ Assert.NotNull(importMap);
+ }
+
+ [Fact]
+ public void WithStaticAssets_DoesNotAddResourceCollection_WhenAlreadyExists()
+ {
+ // Arrange
+ var endpointBuilder = new TestEndpointRouteBuilder();
+ endpointBuilder.MapStaticAssets("TestManifests/Test.staticwebassets.endpoints.json");
+ var conventionBuilder = new TestEndpointConventionBuilder();
+
+ var existingCollection = new ResourceAssetCollection([]);
+ var endpointBuilderInstance = new TestEndpointBuilder();
+ endpointBuilderInstance.Metadata.Add(existingCollection);
+
+ // Act
+ conventionBuilder.WithStaticAssets(endpointBuilder, "TestManifests/Test.staticwebassets.endpoints.json");
+ conventionBuilder.ApplyConventions(endpointBuilderInstance);
+
+ // Assert
+ var collections = endpointBuilderInstance.Metadata.OfType().ToList();
+ Assert.Single(collections);
+ Assert.Same(existingCollection, collections[0]);
+ }
+
+ [Fact]
+ public void WithStaticAssets_AddsResourceCollection_ToEndpoints_DefaultManifest()
+ {
+ // Arrange
+ var endpointBuilder = new TestEndpointRouteBuilder();
+ endpointBuilder.MapStaticAssets();
+ var conventionBuilder = new TestEndpointConventionBuilder();
+
+ // Act
+ conventionBuilder.WithStaticAssets(endpointBuilder);
+
+ // Assert
+ var endpointBuilderInstance = new TestEndpointBuilder();
+ conventionBuilder.ApplyConventions(endpointBuilderInstance);
+
+ var collection = endpointBuilderInstance.Metadata.OfType().FirstOrDefault();
+ Assert.NotNull(collection);
+
+ var list = Assert.IsAssignableFrom>(collection);
+ Assert.Single(list);
+ Assert.Equal("default.css", list[0].Url);
+ }
+
+ private class TestEndpointConventionBuilder : IEndpointConventionBuilder
+ {
+ private readonly List> _conventions = [];
+
+ public void Add(Action convention)
+ {
+ ArgumentNullException.ThrowIfNull(convention);
+ _conventions.Add(convention);
+ }
+
+ public void ApplyConventions(EndpointBuilder endpointBuilder)
+ {
+ foreach (var convention in _conventions)
+ {
+ convention(endpointBuilder);
+ }
+ }
+ }
+
+ private class TestEndpointBuilder : EndpointBuilder
+ {
+ public TestEndpointBuilder()
+ {
+ ApplicationServices = TestEndpointRouteBuilder.CreateServiceProvider();
+ }
+
+ public override Endpoint Build()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ private class TestEndpointRouteBuilder : IEndpointRouteBuilder
+ {
+ private readonly ApplicationBuilder _applicationBuilder;
+
+ public TestEndpointRouteBuilder()
+ {
+ _applicationBuilder = new ApplicationBuilder(ServiceProvider);
+ }
+
+ public IServiceProvider ServiceProvider { get; } = CreateServiceProvider();
+
+ public static IServiceProvider CreateServiceProvider()
+ {
+ var collection = new ServiceCollection();
+ collection.AddSingleton(new ConfigurationBuilder().Build());
+ collection.AddSingleton(new TestWebHostEnvironment());
+ collection.AddRazorComponents();
+ return collection.BuildServiceProvider();
+ }
+
+ public ICollection DataSources { get; } = [];
+
+ public IApplicationBuilder CreateApplicationBuilder()
+ {
+ return _applicationBuilder.New();
+ }
+
+ private class TestWebHostEnvironment : IWebHostEnvironment
+ {
+ public string ApplicationName { get; set; } = "TestApplication";
+ public string EnvironmentName { get; set; } = "TestEnvironment";
+ public string WebRootPath { get; set; } = "";
+ public IFileProvider WebRootFileProvider { get => ContentRootFileProvider; set { } }
+ public string ContentRootPath { get; set; } = Directory.GetCurrentDirectory();
+ public IFileProvider ContentRootFileProvider { get; set; } = CreateTestFileProvider();
+
+ private static TestFileProvider CreateTestFileProvider()
+ {
+ var provider = new TestFileProvider();
+ provider.AddFile("site.css", "body { color: red; }");
+ return provider;
+ }
+ }
+ }
+}
\ No newline at end of file