diff --git a/docs/en/AspNetCore/Dashboards.md b/docs/en/AspNetCore/Dashboards.md
new file mode 100644
index 0000000000..26b6a77461
--- /dev/null
+++ b/docs/en/AspNetCore/Dashboards.md
@@ -0,0 +1,3 @@
+# Dashboards
+
+TODO
\ No newline at end of file
diff --git a/docs/en/AspNetCore/Widgets.md b/docs/en/AspNetCore/Widgets.md
new file mode 100644
index 0000000000..b51ffb901e
--- /dev/null
+++ b/docs/en/AspNetCore/Widgets.md
@@ -0,0 +1,84 @@
+# Widgets
+
+ABP provides a model and infrastructure to create **reusable widgets**. It relies on [ASP.NET Core's ViewComponent](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components) system, so any view component can be used as a widget. Widget system is especially useful when you want to;
+
+* Define widgets in reusable **[modules](../Module-Development-Basics.md)**.
+* Have **scripts & styles** for your widget.
+* Create **[dashboards](Dashboards.md)** with widgets used in (widgets you've built yourself or defined by modules you are using).
+* Co-operate widgets with **[authorization](../Authorization.md)** and **[bundling](Bundling-Minification.md)** systems.
+
+## Basic Widget Definition
+
+### Create a View Component
+
+As the first step, create a regular ASP.NET Core View Component:
+
+
+
+**MySimpleWidgetViewComponent.cs**:
+
+````csharp
+using Microsoft.AspNetCore.Mvc;
+using Volo.Abp.AspNetCore.Mvc;
+
+namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
+{
+ public class MySimpleWidgetViewComponent : AbpViewComponent
+ {
+ public IViewComponentResult Invoke()
+ {
+ return View();
+ }
+ }
+}
+````
+
+**Default.cshtml**:
+
+```xml
+
+```
+
+### Define the Widget
+
+Second step is to define a widget using the `WidgetOptions` (in the `ConfigureServices` method of your Web module):
+
+````csharp
+Configure(options =>
+{
+ options.Widgets.Add(
+ new WidgetDefinition(
+ "MySimpleWidget", //Unique Widget name
+ typeof(MySimpleWidgetViewComponent) //Type of the ViewComponent
+ )
+ );
+});
+````
+
+Widget name should be unique in the application.
+
+*TODO: This is in development and will probably change!*
+
+### Render the Widget
+
+Whenever you want to render a widget, you can inject the `IWidgetRenderer` and use the `RenderAsync` method with the unique widget name.
+
+````xml
+@inject IWidgetRenderer WidgetRenderer
+
+@await WidgetRenderer.RenderAsync(Component, "MySimpleWidget")
+````
+
+You could do the same with standard `Component.InvokeAsync` method. The main difference is that `IWidgetRenderer` uses the widget name defined before. The essential benefit of the `WidgetRenderer` comes when your widget has additional resources, like script and style files.
+
+## Style & Script Dependencies
+
+There are some challenges when your widget has script and style files;
+
+* Any page uses the widget should also include the **its script & styles** files into the page.
+* The page should also care about **depended libraries/files** of the widget.
+
+ABP solves all these issues when you properly relate the resources with the widget. You don't care about dependencies of the widget.
\ No newline at end of file
diff --git a/docs/en/images/widget-basic-files.png b/docs/en/images/widget-basic-files.png
new file mode 100644
index 0000000000..bf5f122d42
Binary files /dev/null and b/docs/en/images/widget-basic-files.png differ
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorListExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollectionExtensions.cs
similarity index 80%
rename from framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorListExtensions.cs
rename to framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollectionExtensions.cs
index 2e2087435b..8261706431 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorListExtensions.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollectionExtensions.cs
@@ -1,6 +1,6 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling
{
- public static class BundleContributorListExtensions
+ public static class BundleContributorCollectionExtensions
{
public static void AddFiles(this BundleContributorCollection contributors, params string[] files)
{
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml
index 6eb41a7546..2e95453185 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml
@@ -9,6 +9,8 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Themes.Basic.Components.PageAlerts
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Components
@using Volo.Abp.AspNetCore.Mvc.UI.Theming
+@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetScripts
+@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles
@using Volo.Abp.MultiTenancy
@inject IAbpAntiForgeryManager AbpAntiForgeryManager
@inject IBrandingProvider BrandingProvider
@@ -39,6 +41,8 @@
@await RenderSectionAsync("styles", false)
+ @await Component.InvokeAsync(typeof(WidgetStylesViewComponent))
+
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.Last, StandardLayouts.Account)
@@ -50,7 +54,7 @@
@if (MultiTenancyOptions.Value.IsEnabled &&
- (TenantResolveResultAccessor.Result?.AppliedResolvers?.Contains(CookieTenantResolveContributor.ContributorName) == true))
+ (TenantResolveResultAccessor.Result?.AppliedResolvers?.Contains(CookieTenantResolveContributor.ContributorName) == true))
{
@MultiTenancyStringLocalizer["Tenant"]:
@@ -80,6 +84,8 @@
@await RenderSectionAsync("scripts", false)
+ @await Component.InvokeAsync(typeof(WidgetScriptsViewComponent))
+
@await Component.InvokeLayoutHookAsync(LayoutHooks.Body.Last, StandardLayouts.Account)