Bundling static assets of builder components
Components developed for the system’s page and form builder features (such as widgets) often require custom CSS and JavaScript assets to achieve their desired appearance and functionality. With complex components, the number of such assets quickly multiplies and it becomes impractical to include them on pages individually. Additionally, keeping request count and static asset size low helps improve page performance. For these reasons, we recommend using bundling and minification for your static assets.
With the Xperience ASP.NET Core integration, use the following process to implement bundling and minification:
- Choose and set up a bundling and minification process suitable for your needs and workflow. The implementation and used tools are completely in your hands.
- Create bundles for your component assets and provide them to Xperience:
Create separate CSS and JS bundles for both the page and form builder.
Create separate bundles for the live site and the Xperience administration interface. Certain assets, such as Inline editor registration scripts, are only required when using the page builder editing interface in the administration application and vice versa.
The bundles must have specific names, be placed into designated locations within the project’s web root, and contain assets depending on the type of the bundle.
See the Bundles section for detailed information about the default bundle names and locations.
(Optional) If you do not wish to use the default bundle names and locations, you can specify a custom location and sources for individual bundles.
The system includes and renders the resulting bundles on pages where builder functionality is enabled:
- For the page builder on pages that call the PageBuilderScripts and PageBuilderStyles extension methods.
- For the form builder automatically when creating forms in the administration interface and on pages containing forms.
Example
The Sample bundling and minification process section demonstrates how to configure a simple bundling process using the Grunt automation library. The process bundles files from the default source directories and places the created bundles into the default locations.
System bundling features
Handling missing bundles
The system stores information about the source directories of files that comprise each bundle. If a bundle is not found in the expected location, files from the corresponding source directories are linked individually as a fallback. Every file results in a new script tag on the page. This can be useful, for example, for debugging purposes in a development environment.
Each bundle has a preconfigured default source directory (listed in the Bundles section). If necessary, you can add additional source directories via provided options classes .
Bundle caching
The system automatically caches served bundles to reduce application overhead.
Note: If your bundling process creates an empty file for a bundle, the given bundle is not included in the page at all. This information is also cached by the system to reduce unnecessary overhead. However, adding content to a previously empty bundle requires clearing of the cache to ensure the change is reflected on the site (e.g., by restarting the application).
Bundles
The following sections describe the default names and locations required for individual bundle types:
Complementary system bundles
For each bundle described below, the system automatically includes a bundle containing the scripts and styles required by the system’s default page and form builder components, such as the Form widget and individual form components. These bundles can be identified by their system prefix and the rules for their inclusion are the same as those for user-created bundles.
Page builder bundles
Static assets for custom page builder components need to be split between the following types of bundles:
Bundle | Bundle name | Bundle location (~/wwwroot/Content/Bundles/) | Bundle source directory (within ~/wwwroot/) | Bundle contents |
Administration scripts | pageComponents.min.js | Admin/pageComponents.min.js | PageBuilder/Admin/**/*.js | Included in the administration interface when editing pages that use the page builder feature. The bundle must contain inline editor registration and modal dialog configuration scripts together with any other functionality you wish to enable for your components within the page builder editing interface that is not required on the live site. For a clean directory structure, we recommend placing scripts comprising the bundle into individual directories based on component type. For example:
|
Administration styles | pageComponents.min.css | Admin/pageComponents.min.css | PageBuilder/Admin/**/*.css | Included in the administration interface when editing pages that use the page builder feature. The bundle must contain all styles you wish to use for your components in the page builder editing interface. For a clean directory structure, we recommend placing styles comprising the bundle into individual directories based on component type. For example:
|
Live site scripts | pageComponents.min.js | Public/pageComponents.min.js | PageBuilder/Public/**/*.js | Included on the live site. The bundle must contain all scripts required for the correct functionality of your custom page builder components on the live site. For a clean directory structure, we recommend placing scripts comprising the bundle into individual directories based on component type. For example:
|
Live site styles | pageComponents.min.css | Public/pageComponents.min.css | PageBuilder/Public/**/*.css | Included on the live site. The bundle must contain all styles required to correctly display custom components. For a clean directory structure, we recommend placing styles comprising the bundle into individual directories based on component type. For example:
|
Form builder bundles
Static assets for custom form builder components need to be split into three bundles:
- A bundle containing component scripts.
- Two bundles containing component styles: one for the live site, the other for the administration interface.
Bundle | Bundle name | Bundle location (within ~/wwwroot/Content/Bundles/) | Bundle source directory (within ~/wwwroot/) | Bundle contents |
Administration styles | formComponents.min.css | Admin/formComponents.min.css | FormBuilder/Admin/**/*.css | Included when accessing the form builder editing interface in the administration application. The bundle must contain all styles you wish to use for your form components in the form builder editing interface in the Xperience administration. Note that the system already attempts to enforce a unified look and feel for components rendered in the form builder interface. See Developing form components and the GetEditorHtmlAttributes extension method. For a clean directory structure, we recommend placing styles comprising the bundle into individual directories. For example:
|
Administration and live site scripts | formComponents.min.js | Public/formComponents.min.js | FormBuilder/Public/**/*.js | Included on pages with forms and when accessing the form builder editing interface in the administration application. The bundle must contain all scripts required for the correct functionality of your custom form builder components. There is no dedicated bundle for the administration interface. This bundle is linked both when rendering forms on the live site (via the Form widget ) and when composing forms via the Form builder interface . For a clean directory structure, we recommend placing scripts comprising the bundle into individual directories. For example:
|
Live site styles | formComponents.min.css | Public/formComponents.min.css | FormBuilder/Public/**/*.css | Included on pages with forms. The bundle must contain all styles required to correctly display your form builder components on the live site. For a clean directory structure, we recommend placing styles comprising the bundle into individual directories. For example:
|
Sample bundling and minification process
The following examples demonstrates a bundling and minification process using Node.js with the Grunt JavaScript task runner for automation. The process cleans all output directories, concatenates the CSS and JS files from relevant directories, performs minification, and outputs the resulting bundles into the default directories expected by the system.
The process uses the following Grunt plugins:
- grunt-contrib-clean for output directory cleaning
- grunt-contrib-concat for bundling
- grunt-contrib-cssmin for CSS minification
- grunt-terser for JavaScript minification
First, add Grunt together with the plugins to your project’s package.json file, create a new Gruntfile.js and load the plugins:
module.exports = function (grunt) {
// Loads required Grunt plugins
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-terser');
grunt.initConfig({
// Contains task definitions
});
}
Define the tasks used to create the individual bundles:
grunt.initConfig({
// Cleans all bundle output directories
clean: {
formBuilder: ['wwwroot/Content/Bundles/Public/formComponents.css', 'wwwroot/Content/Bundles/Public/formComponents.min.css', 'wwwroot/Content/Bundles/Admin/formComponents.css',
'wwwroot/Content/Bundles/Admin/formComponents.min.css', 'wwwroot/Content/Bundles/Public/formComponents.js', 'wwwroot/Content/Bundles/Public/formComponents.min.js'],
pageBuilder: ['wwwroot/Content/Bundles/Public/pageComponents.css', 'wwwroot/Content/Bundles/Public/pageComponents.min.css', 'wwwroot/Content/Bundles/Admin/pageComponents.css',
'wwwroot/Content/Bundles/Admin/pageComponents.min.css', 'wwwroot/Content/Bundles/Public/pageComponents.js', 'wwwroot/Content/Bundles/Public/pageComponents.min.js',
'wwwroot/Content/Bundles/Admin/pageComponents.js', 'wwwroot/Content/Bundles/Admin/pageComponents.min.js']
},
// Bundles files from the default source directories
concat: {
formBuilder: {
files: {
// Styles for the live
'wwwroot/Content/Bundles/Public/formComponents.css': ['wwwroot/FormBuilder/Public/**/*.css'],
// Styles for the administration interface
'wwwroot/Content/Bundles/Admin/formComponents.css': ['wwwroot/FormBuilder/Admin/**/*.css'],
// Scripts included on both the live site and administration
'wwwroot/Content/Bundles/Public/formComponents.js': ['wwwroot/FormBuilder/Public/**/*.js']
}
},
pageBuilder: {
files: {
// Styles for the live site
'wwwroot/Content/Bundles/Public/pageComponents.css': ['wwwroot/PageBuilder/Public/**/*.css'],
// Styles for the administration interface
'wwwroot/Content/Bundles/Admin/pageComponents.css': ['wwwroot/PageBuilder/Admin/**/*.css'],
// Scripts for the live site
'wwwroot/Content/Bundles/Public/pageComponents.js': ['wwwroot/PageBuilder/Public/**/*.js'],
// Scripts for the administration
'wwwroot/Content/Bundles/Admin/pageComponents.js': ['wwwroot/PageBuilder/Admin/**/*.js']
}
}
},
// Minifies the resulting CSS bundles
cssmin: {
formBuilder: {
files: {
'wwwroot/Content/Bundles/Public/formComponents.min.css': 'wwwroot/Content/Bundles/Public/formComponents.css',
'wwwroot/Content/Bundles/Admin/formComponents.min.css': 'wwwroot/Content/Bundles/Admin/formComponents.css'
}
},
pageBuilder: {
files: {
'wwwroot/Content/Bundles/Public/pageComponents.min.css': ['wwwroot/Content/Bundles/Public/pageComponents.css'],
'wwwroot/Content/Bundles/Admin/pageComponents.min.css': ['wwwroot/Content/Bundles/Admin/pageComponents.css']
}
}
},
// Minifies the resulting JS bundles
terser: {
formBuilder: {
files: {
'wwwroot/Content/Bundles/Public/formComponents.min.js': ['wwwroot/Content/Bundles/Public/formComponents.js']
}
},
pageBuilder: {
files: {
'wwwroot/Content/Bundles/Public/pageComponents.min.js': ['wwwroot/Content/Bundles/Public/pageComponents.js'],
'wwwroot/Content/Bundles/Admin/pageComponents.min.js': ['wwwroot/Content/Bundles/Admin/pageComponents.js']
}
}
}
});
Finally, register alias tasks that batch the created tasks for convenience:
// Running the 'formBuilder' and 'pageBuilder' tasks automatically executes all tasks provided in the array
grunt.registerTask('formBuilder', ['clean:formBuilder', 'concat:formBuilder', 'cssmin:formBuilder', 'terser:formBuilder']);
grunt.registerTask('pageBuilder', ['clean:pageBuilder', 'concat:pageBuilder', 'cssmin:pageBuilder', 'terser:pageBuilder']);
Using the created tasks, you can automate the bundling and minification process for your static assets.
Customizing bundle locations and sources
You can customize the location where Xperience expects bundles and their contents (to be linked individually, in case the bundle is not found). For this purpose, the system provides the PageBuilderBundlesOptions and FormBuilderBundlesOptions options classes.
The options classes expose properties that allow you to configure the corresponding bundle:
- PageBuilderBundlesOptions
- FormBuilderBundlesOptions
These properties can be configured using the conventional ASP.NET Core options pattern. See the following sections for details about specific configuration options:
- Changing the bundle location and name
- Adding source directories for a bundle
- Replacing the default jQuery libraries used by the form builder
Changing the bundle location and name
You can change the file name and path where the system looks for individual bundles. Set the WebRootBundlePath property provided by the individual bundle objects within the options classes.
For example, the following code demonstrates how to set a custom location for the FormBuilderPublicStyles bundle via the ConfigureServices method within the application’s startup class:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<FormBuilderBundlesOptions>(options =>
{
// Sets a custom path to the style sheet bundle used by form builder components on the live site
// The specified path implicitly starts from the application's web root (~/wwwroot/ folder)
options.FormBuilderPublicStyles.WebRootBundlePath = "My/Custom/Bundle/Path.min.css";
});
...
}
Adding source directories for a bundle
If the default directories (described in the page and form builder bundles sections) are not sufficient, bundle options allow you to add additional sources for each bundle. The system links all files from the specified directories if a corresponding bundle is not found. See Handling missing bundles.
Add new directories to the list provided via the Contents.IncludedWebRootDirectories property of the bundle you wish to configure.
For example, the following code adds ~/wwwroot/Some/Directory to the list of sources for the bundle of page builder component styles linked on the live site. If the corresponding bundle is not found, the system includes the CSS files from the specified directory as well:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<PageBuilderBundlesOptions>(options =>
{
// Adds a source directory for the bundle of styles used by page builder components on the live site
// All added paths implicitly start from the application's web root (~/wwwroot/ folder)
options.PageBuilderPublicStyles.Contents.IncludedWebRootDirectories.Add("Some/Directory");
});
...
}
Replacing the default form builder jQuery libraries
By default, the system links two jQuery bundles used in the form and page builder and their default components:
- jquery-3.5.1.js
- jquery.unobtrusive-ajax.js
You can disable the use of jQuery for the form and page builder by setting the CMSBuilderScriptsIncludeJQuery key to false in the configuration file of your live site project (appsettings.json or web.config). All default features remain functional without jQuery.
If you wish to use jQuery, but require a different version, you need to specify the version for both form builder and page builder.
Form builder
Specify the path to your custom jQuery bundles using the
- JQueryCustomBundleWebRootPath
- JQueryUnobtrusiveAjaxCustomBundleWebRootPath
properties of the FormBuilderBundlesOptions object:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<FormBuilderBundlesOptions>(options =>
{
// Specifies custom jQuery bundles to link instead of the system defaults
// All paths implicitly start from the application's web root (~/wwwroot/ folder)
options.JQueryCustomBundleWebRootPath = "Path/To/My/jQuery/Bundle";
options.JQueryUnobtrusiveAjaxCustomBundleWebRootPath = "Path/To/My/jQueryUnobtrusive/Bundle";
});
...
}
When you set a custom path using one of these properties, the system no longer links the bundle in the page builder and you need to manually link your custom bundles as described below.
Page builder
Specify the path to your custom jQuery bundles in the layout of your pages:
<script src="Path/To/My/jQuery/Bundle"></script>
<script src="Path/To/My/jQueryUnobtrusive/Bundle"></script>